#include "../headers/fileSystem.h"

static int getFATPart( uint32_t **ptrFATPart, uint32_t secIni);/*Test OK*/
static uint32_t cnvAStartCluster( uint16_t highBytes, uint16_t lowBytes);

extern char *buffer;
extern uint32_t tamBuffer;
extern bsUtil bsu;
extern uint32_t *ptrFAT;
extern uint8_t *ptrBMP;


/*Test OK*/
int getBootSector( bootSector **bs, fsInformationSector **fsIs){

	int x;

	x = getSector( 0, buffer);
	*bs = (bootSector *)buffer;

	x = getSector( (*bs)->sectorNumberOfFsInfSector, buffer + TAM_SECTOR * (*bs)->sectorNumberOfFsInfSector);
	*fsIs = (fsInformationSector *)(buffer + TAM_SECTOR * (*bs)->sectorNumberOfFsInfSector);

    return 0;
}
/*=====================================================================================*/
/*Test OK*/
int procesarBootSector( bootSector **bs, fsInformationSector **fsIs){

    bsu.bytesPerSector = (*bs)->bytesPerSector;
    bsu.firstClusterOfRootDirectory = (*bs)->firstClusterOfRootDirectory;
    bsu.mostRecentlyAllocatedCluster = (*fsIs)->mostRecentlyAllocatedCluster;
    bsu.numberOfFats = (*bs)->numberOfFats;
    bsu.numberOfFreeClusters = (*fsIs)->numberOfFreeClusters;
    bsu.reservedSectorCount = (*bs)->reservedSectorCount;
    bsu.sectorNumberOfFsInfSector = (*bs)->sectorNumberOfFsInfSector;
    bsu.sectorsPerCluster = (*bs)->sectorsPerCluster;
    bsu.sectorsPerFat = (*bs)->sectorsPerFat;
    bsu.totalSectors = (*bs)->totalSectors;
    bsu.totalSectors2 = (*bs)->totalSectors2;
    bsu.totalClusters = bsu.totalSectors2 / bsu.sectorsPerCluster;

    if( bsu.totalSectors2 % bsu.sectorsPerCluster)
    	bsu.totalClusters++;

    return 0;
}
/*=====================================================================================*/
/*Test OK*/
int crearFAT( void){

	uint32_t x = bsu.sectorsPerFat * (bsu.bytesPerSector / 4);  /* Cantidad de entradas de la FAT */
	int i = 0;
	uint32_t *ptr;

	ptrFAT = ( uint32_t *)malloc( bsu.sectorsPerFat * bsu.bytesPerSector);

	for( ptr = ptrFAT; i < x; *(ptr + i) = 0x00000000, i++);

	return 0;
}
/*=====================================================================================*/
/*Test OK*/
int crearBitMap( void){

	uint32_t x = bsu.totalClusters/*bsu.sectorsPerFat * (bsu.bytesPerSector / 4)*/;  /* Cantidad de entradas de la FAT */
	uint32_t res, i;
	uint8_t *ptr;

    res = x % 8;
    x /= 8;

    if(res)
        x++;

    bsu.tamBitMap = x;

	ptrBMP = ( uint8_t *)malloc( x);

	for( i = 0, ptr = ptrBMP; i < x; *(ptr + i) = 0xff, i++);

	return 0;
}
/*=====================================================================================*/
/*Test OK*/
int inicializarFATyBitMap( void){

	int i, x;
	uint32_t *ptrFATPart;
	uint32_t secIni = bsu.reservedSectorCount;
	uint32_t cantFATParts = bsu.sectorsPerFat / (tamBuffer / bsu.bytesPerSector);


	if(bsu.sectorsPerFat % (tamBuffer / bsu.bytesPerSector))
		cantFATParts++;

	for( i = 0; i <  cantFATParts; i++){

		x = getFATPart( &ptrFATPart, secIni + i * tamBuffer / bsu.bytesPerSector);

		x = copyBufToFATeInicBMP( &ptrFATPart, i);
	}

	bsu.EOC = *(ptrFAT + 1);

	return 0;
}
/*=====================================================================================*/
/*Test OK*/
int delFAT( void){

	free( ptrFAT);

	return 0;
}
/*=====================================================================================*/
/*Test OK*/
int delBitMap( void){

    free( ptrBMP);

	return 0;
}
/*=====================================================================================*/
/*Test OK*/
static int getFATPart( uint32_t **ptrFATPart, uint32_t secIni){

	int i;

	for( i = 0; i < tamBuffer / bsu.bytesPerSector; getSector( secIni + i, buffer + i * bsu.bytesPerSector), i++);

	*ptrFATPart = ( uint32_t *)buffer;

	return 0;
}
/*=====================================================================================*/
int getDirectoryTable( directoryTableEntry **dirTable, char *idBloque, uint32_t numCluster){

	char *buf;


	getBloque(numCluster, &buf, idBloque);

	*dirTable = (directoryTableEntry *)buf;
	return 0;
}
/*=====================================================================================*/
/********************************************************************************************/
/*                 uint32_t recorrerPath( char *path, uint32_t numCluster)                  */
/*                                                                                          */
/* Devuelve el numero de cluster de inicio del archivo o directorio                         */
/* numCluster en la llamada a la funcion debe ser igual a 2                                 */
/* No se contempla que el path sea el directorio raiz o un subdirectorio seguido de una "/" */
/* ejemplo:                                                                                 */
/* ls /        -> No utilizar esta funcion en este caso                                     */
/* ls /dir1/   -> Eliminar la ultima barra antes de llamar a la funcion                     */
/*                                                                                          */
/********************************************************************************************/
uint32_t recorrerPath( char *path, uint32_t numCluster){

	int x, i = 1;
	char nom[14];
	char idBlock;
	directoryTableEntry *dirTable;
	lstDirTable *listaDir;
	tDirEntry infoArc;


	for( ; *(path + i) != '/' && *(path + i) != '\0'; i++);

	memcpy( nom, path + 1, i - 1);

	getDirectoryTable( &dirTable, &idBlock,numCluster);

	armarListaDir( dirTable, &listaDir);

	liberarIdBuffer(idBlock);

	x = buscarArchEnLista( nom, listaDir, &infoArc);

	liberarListaArchivos( listaDir);

	if(x == FIN_ERROR)
			return 0;

	if( *(path + i) == '/')
		return recorrerPath( path + i, infoArc.startFileCluster);
	else
		return infoArc.startFileCluster;
}
/*=====================================================================================*/
void armarListaDir( directoryTableEntry *dirTable, lstDirTable **lista){

	directoryTableEntry *ptr = dirTable;
	lfnEntry *ptrAux;
	tDirEntry dirEnt;
	uint32_t i;
	char salir, ignorar;


	crearListaDirTable( &(*lista));

	for( i = salir = ignorar = 0; !salir; i++){
		if((ptr + i)->dosFileName[0] == 0x00)
			salir = ignorar = 1;
		else if((ptr + i)->dosFileName[0] == 0xE5)
			ignorar = 1;

		if(!ignorar){
			if((ptr + i)->fileAttributes == 0x0F){
				ptrAux = (lfnEntry *)(ptr + i);
				i++;
				readLFNEntry( ptrAux, &dirEnt);
				readDirEntry( ptr + i, &dirEnt);
			}
			else{
				memcpy(dirEnt.name, (ptr + i)->dosFileName, 8);
				readDirEntry( ptr + i, &dirEnt);
			}
			dirEnt.posRelClusterDataRegion = i;
			insertaNodoDirTable( &(*lista), dirEnt);
		}
		else
			ignorar = 0;
	}

	return;
}
/*=====================================================================================*/
void readDirEntry( directoryTableEntry *bufDirEntry, tDirEntry *dirEntry){

	dirEntry->fileAttributes = bufDirEntry->fileAttributes;
	dirEntry->fileSize = bufDirEntry->fileSize;
	dirEntry->startFileCluster = cnvAStartCluster(bufDirEntry->startFileCluster, bufDirEntry->startFileCluster2);

	return;
}
/*=====================================================================================*/
void readLFNEntry( lfnEntry *bufDirEntry, tDirEntry *dirEntry){

	size_t tamUTF8;

	unicode_utf16_to_utf8_inbuffer( bufDirEntry->nameCharacters, 5, (char *) dirEntry->name, &tamUTF8);
	unicode_utf16_to_utf8_inbuffer( bufDirEntry->nameCharacters2, 6, (char *) dirEntry->name + 5, &tamUTF8);
	unicode_utf16_to_utf8_inbuffer( bufDirEntry->nameCharacters3, 2, (char *) dirEntry->name + 11, &tamUTF8);

	dirEntry->name[13] = '\0';

	strtok( (char *)dirEntry->name, " ");

	return;
}
/*=====================================================================================*/
static uint32_t cnvAStartCluster( uint16_t highBytes, uint16_t lowBytes){

	uint32_t x;

	x = (2^16)*lowBytes + highBytes;

	return swap_uint32(x);
}
/*=====================================================================================*/
int buscarArchEnLista( char *nombreArchivo, struct lstDirTable *listaArchivos, struct tDirEntry *archivo){

	struct lstDirTable *ptr;

	ptr = listaArchivos;

	while(ptr && strcmp((char *) ptr->info.name, nombreArchivo))
		ptr = ptr->sgte;

	if (ptr)
		*archivo = ptr->info;
	else
		return FIN_ERROR;

	return FIN_OK;
}
/*=====================================================================================*/
int liberarListaArchivos(struct lstDirTable *listaArchivos){

	struct lstDirTable *ptr;

	while(listaArchivos){
		ptr = listaArchivos;
		listaArchivos = listaArchivos->sgte;
		free(ptr);
	}

	return FIN_OK;
}
/*=====================================================================================*/
int listarPath( char *path, struct lstDirTable **listaArchivos){

	uint32_t nroCluster;
	directoryTableEntry *bufBloque;
	char idBloque;


	if( strlen(path) > 1){

		if(path[strlen(path)-1] == '/')
			path[strlen(path)-1] = '\0';

		nroCluster = recorrerPath( path, 2);
	}
	else
		nroCluster = 2;

	getDirectoryTable( &bufBloque, &idBloque, nroCluster);

	armarListaDir(bufBloque, &(*listaArchivos));

	liberarIdBuffer(idBloque);

	return FIN_OK;
}
/*=====================================================================================*/
int obtenerNombreDeArchivo( char *path, char *nombreArchivo){

	char *ptr;
	char *nombre;

	for(ptr = path; ptr; ptr++)
		if (*ptr == '/')
			nombre = ptr + 1;

	strcpy(nombreArchivo, nombre);

	return FIN_OK;
}
/*=====================================================================================*/
int obtenerInfoArchivo( char *path, struct tDirEntry *archivo){

	struct lstDirTable *listaArchivos;
	char nombreArchivo[14];

	listarPath(path, &listaArchivos);

	obtenerNombreDeArchivo(path, nombreArchivo);

	if (buscarArchEnLista(nombreArchivo, listaArchivos, archivo))
		return FIN_ERROR;

	liberarListaArchivos(listaArchivos);

	return FIN_OK;
}
/*=====================================================================================*/
int ultClusterArch( uint32_t firstCluster, uint32_t *lastCluster){

	uint32_t *ptr = ptrFAT;
	uint32_t i;

	for( i = firstCluster; *(ptr + i) != bsu.EOC; i = *(ptr + i));

	*lastCluster = i;

	return FIN_OK;
}
/*=====================================================================================*/
int recorrerXClustersArch( uint32_t firstCluster, uint32_t nuevaCantClusters, uint32_t *ultCluster){

	uint32_t *ptr = ptrFAT;
	uint32_t i,j;

	for( i = firstCluster, j = 0; *(ptr + i) != bsu.EOC && j < nuevaCantClusters - 1; i = *(ptr + i), j++);

	*ultCluster = i;

	return FIN_OK;
}
/*=====================================================================================*/
int actualizarTamanioArchEnFAT( size_t size, uint32_t nroCluster, uint32_t posRelCluster, char *nombreArchivo){

	directoryTableEntry *bufBloque;
	char idBloque;


	getDirectoryTable( &bufBloque, &idBloque, nroCluster);

	(bufBloque + posRelCluster)->fileSize = size;

	setBloque( nroCluster,idBloque);

	return FIN_OK;
}

















